import { TemplateItemConfig } from 'src/app/services/print.const';
import { Injectable } from "@angular/core";
import { BLE } from "@ionic-native/ble/ngx";
import { Observable } from 'rxjs';
import { PrintInstruction } from "./print.const";

@Injectable({
	providedIn: "root"
})
export class BluetoothPrinterService {

	private device: any;
	private characteristic: any;

	constructor(
		private ble: BLE) {
		this.device = {};
		this.characteristic = {};

		setInterval(async () => {
			if (this.device.id) {
				const conneted = await this.isConnected(this.device.id);
				console.log(conneted)
				if (!conneted) {
					this.save({});
					this.device = {};
				}
			}
		}, 1000)

	}

	/**
	 * 获取已经缓存的设备
	 *
	 * @returns
	 * @memberof BluetoothPrinterService
	 */
	getDevice() {
		return this.device;
	}

	/**
	 * 是否存在已保存的蓝牙设备
	 *
	 * @returns {boolean} true:存在, false:不存在
	 * @memberof BluetoothPrinterService
	 */
	hasDevice(): boolean {
		return !!this.device.id;
	}

	save(device: any) {
		this.device = device;
		this.characteristic = this.getPrinterCharacteristic(device);

		console.log(this.characteristic);
	}

	isPrinter(device: any) {
		let printer = false;
		if (device.characteristics && device.characteristics.length > 0) {
			for (const characteristic of device.characteristics) {
				if (
					// characteristic.properties.indexOf("Read") > -1 &&
					characteristic.properties.indexOf("WriteWithoutResponse") > -1
					&& characteristic.properties.indexOf("Write") > -1
					&& characteristic.characteristic.length > 4
					// && characteristic.properties.indexOf("Notify") > -1
					// && characteristic.properties.indexOf("Indicate") > -1
				) {
					printer = true;
				}
			}
		}
		return printer;
	}

	private getPrinterCharacteristic(device: any): any {
		if (device.characteristics && device.characteristics.length > 0) {
			for (const characteristic of device.characteristics) {
				if (
					// characteristic.properties.indexOf("Read") > -1 &&
					characteristic.properties.indexOf("WriteWithoutResponse") > -1
					&& characteristic.properties.indexOf("Write") > -1
					&& characteristic.characteristic.length > 4
					// && characteristic.properties.indexOf("Notify") > -1
					// && characteristic.properties.indexOf("Indicate") > -1
				) {
					return characteristic;
				}
			}
		}
		return {};
	}

	startScan(): Observable<any> {
		return this.ble.startScan([]);
	}

	stopScan(): Promise<any> {
		return this.ble.stopScan();
	}

	/**
	 * 是否存在已连接的蓝牙设备
	 *
	 * @returns {boolean} true:存在, false:不存在
	 * @memberof BluetoothPrinterService
	 */
	isConnected(deviceId: string): Promise<boolean> {
		return this.ble.isConnected(deviceId).then(() => true, () => false);
	}

	/**
	 * 连接蓝牙设备
	 *
	 * @param {*} device
	 * @returns {Promise<any>}
	 * @memberof BluetoothPrinterService
	 */
	connect(deviceId: string): Observable<any> {
		return this.ble.connect(deviceId);
	}

	/**
	 * 连接蓝牙设备
	 *
	 * @param {*} device
	 * @returns {Promise<any>}
	 * @memberof BluetoothPrinterService
	 */
	disconnect(deviceId: string): Promise<any> {
		return this.ble.disconnect(deviceId);
	}

	/**
	 * 判断设备的蓝牙是否打开
	 *
	 * @returns {Promise<boolean>}
	 * @memberof BluetoothPrinterService
	 */
	isEnabled(): Promise<boolean> {
		return this.ble.isEnabled().then(() => true, () => false);
	}

	/**
	 * 打开设备的蓝牙设置界面
	 *
	 * @returns {Promise<boolean>}
	 * @memberof BluetoothPrinterService
	 */
	async openBluetoothSetting() {
		await this.ble.showBluetoothSettings();
	}

	private numsToArray(arr: number[]) {
		const ba = new Uint8Array(arr.length);
		for (let i = 0; i < arr.length; i++) {
			ba[i] = arr[i];
		}
		return ba.buffer;
	}

	/**
	 * 初始化打印机，清除打印缓冲区，恢复默认值，选择字符打印方式，删除用户自定义字符
	 *
	 * @memberof BluetoothPrinterService
	 */
	async initPrinter() {
		await this.ble.writeWithoutResponse(this.device.id, this.characteristic.service, this.characteristic.characteristic, this.numsToArray(PrintInstruction.INIT));
	}

	/**
	 * 追加文本到缓存中，不强制打印
	 *
	 * @param {string} text 要打印的内容
	 * @memberof BluetoothPrinterService
	 */
	async printTextWithoutFeed(text: string) {
		await this.ble.writeWithoutResponse(this.device.id, this.characteristic.service, this.characteristic.characteristic, this.numsToArray(this.toUTF8Array(text)));
	}


	/**
	 * 打印文本并换行
	 *
	 * @param {string} text 要打印的内容
	 * @memberof BluetoothPrinterService
	 */
	async printText(text: string) {
		await this.ble.writeWithoutResponse(this.device.id, this.characteristic.service, this.characteristic.characteristic, this.numsToArray(this.toUTF8Array(text)));
		await this.ble.writeWithoutResponse(this.device.id, this.characteristic.service, this.characteristic.characteristic, this.numsToArray(PrintInstruction.LF));
	}

	/**
	 * 打印文本并走纸
	 *
	 * @param {string} text 要打印的内容
	 * @param {number} n 走纸行数
	 * @memberof BluetoothPrinterService
	 */
	async printTextAndFeed(text: string, n: number) {
		await this.ble.writeWithoutResponse(this.device.id, this.characteristic.service, this.characteristic.characteristic, this.numsToArray(this.toUTF8Array(text)));
		await this.ble.writeWithoutResponse(this.device.id, this.characteristic.service, this.characteristic.characteristic, this.numsToArray(PrintInstruction.ESC_d_n.concat([n])));
	}


	/**
	 * 重置行间距为 32 点行
	 *
	 * @memberof BluetoothPrinterService
	 */
	async resetTextLineSpace() {
		await this.ble.writeWithoutResponse(this.device.id, this.characteristic.service, this.characteristic.characteristic, this.numsToArray(PrintInstruction.ESC_2));
	}

	/**
	 * 重置行间距为 n 点行
	 *
	 * @param {number} n 点行
	 * @memberof BluetoothPrinterService
	 */
	async setTextLineSpace(n: number) {
		await this.ble.writeWithoutResponse(this.device.id, this.characteristic.service, this.characteristic.characteristic, this.numsToArray(PrintInstruction.ESC_3_n.concat([n])));
	}

	/**
	 * 重置字间距为 n 点
	 *
	 * @param {number} n 点
	 * @memberof BluetoothPrinterService
	 */
	async setTextLetterSpace(n: number) {
		await this.ble.writeWithoutResponse(this.device.id, this.characteristic.service, this.characteristic.characteristic, this.numsToArray(PrintInstruction.ESC_SP_n.concat([n])));
	}


	/**
	 * 设置打印左对齐
	 *
	 * @memberof BluetoothPrinterService
	 */
	async setAlignLeft() {
		await this.ble.writeWithoutResponse(this.device.id, this.characteristic.service, this.characteristic.characteristic, this.numsToArray(PrintInstruction.ESC_a_0));
	}

	/**
	 * 设置打印居中对齐
	 *
	 * @memberof BluetoothPrinterService
	 */
	async setAlignCenter() {
		await this.ble.writeWithoutResponse(this.device.id, this.characteristic.service, this.characteristic.characteristic, this.numsToArray(PrintInstruction.ESC_a_1));
	}

	/**
	 * 设置打印右对齐
	 *
	 * @memberof BluetoothPrinterService
	 */
	async setAlignRight() {
		await this.ble.writeWithoutResponse(this.device.id, this.characteristic.service, this.characteristic.characteristic, this.numsToArray(PrintInstruction.ESC_a_2));
	}

	/**
	 * 设置打印字符格式，不传参数则为重置格式
	 *
	 * @param {boolean} [bold] 加粗
	 * @param {boolean} [doubleHeight] 双倍高度
	 * @param {boolean} [doubleWidth] 双倍宽度
	 * @memberof BluetoothPrinterService
	 */
	async setTextFormat(
		bold?: boolean,
		doubleHeight?: boolean,
		doubleWidth?: boolean
	) {
		let n = 0x00;
		n |= bold ? 0x08 : 0;
		n |= doubleHeight ? 0x10 : 0;
		n |= doubleWidth ? 0x20 : 0;
		await this.ble.writeWithoutResponse(this.device.id, this.characteristic.service, this.characteristic.characteristic, this.numsToArray(PrintInstruction.FORMAT.concat([n])));
	}

	/**
	 * 重置条形码高度为 n 点，默认不传是50
	 *
	 * @param {number} n 点
	 * @memberof BluetoothPrinterService
	 */
	async setBarcodeHeight(n?: number) {
		await this.ble.writeWithoutResponse(this.device.id, this.characteristic.service, this.characteristic.characteristic, this.numsToArray(PrintInstruction.ESC_SP_n.concat([n ? n : 50])));
	}

	/**
	 * 重置二维码大小为 n
	 * 1 ≤ n ≤ 16
	 * @param {number} n
	 * @memberof BluetoothPrinterService
	 */
	async setQRCodeSize(n: number) {
		await this.ble.writeWithoutResponse(this.device.id, this.characteristic.service, this.characteristic.characteristic, this.numsToArray(PrintInstruction.QRCODE_SIZE.concat([n])));
	}

	/**
	 * 打印二维码
	 * @param {string} text
	 * @memberof BluetoothPrinterService
	 */
	async printQRCode(text: string) {
		const data = this.toUTF8Array(text);
		const directive = PrintInstruction.QRCODE;
		directive[3] += data.length;
		await this.ble.writeWithoutResponse(this.device.id, this.characteristic.service, this.characteristic.characteristic, this.numsToArray(directive.concat(data)));
	}


	private toUTF8Array(text: string) {
		const utf8: number[] = [];
		for (let i = 0; i < text.length; i++) {
			let charcode = text.charCodeAt(i);
			if (charcode < 0x80) {
				utf8.push(charcode);
			} else if (charcode < 0x800) {
				utf8.push(0xc0 | (charcode >> 6),
					0x80 | (charcode & 0x3f));
			} else if (charcode < 0xd800 || charcode >= 0xe000) {
				utf8.push(0xe0 | (charcode >> 12),
					0x80 | ((charcode >> 6) & 0x3f),
					0x80 | (charcode & 0x3f));
			} else {
				i++;
				// UTF-16 encodes 0x10000-0x10FFFF by
				// subtracting 0x10000 and splitting the
				// 20 bits of 0x0-0xFFFFF into two halves
				charcode = 0x10000 + (((charcode & 0x3ff) << 10)
					| (text.charCodeAt(i) & 0x3ff));
				utf8.push(0xf0 | (charcode >> 18),
					0x80 | ((charcode >> 12) & 0x3f),
					0x80 | ((charcode >> 6) & 0x3f),
					0x80 | (charcode & 0x3f));
			}
		}
		return utf8;
	}

	async print(configItems: TemplateItemConfig[], isTemplate?: boolean) {
		console.log(configItems)
		for (const item of configItems) {
			switch (item.align) {
				case "left":
					await this.setAlignLeft();
					break;
				case "center":
					await this.setAlignCenter();
					break;
				case "right":
					await this.setAlignRight();
					break;
			}
			switch (item.type) {
				case "text":
					await this.setTextFormat(item.bold, item.doubleHeight, item.doubleWidth);
					await this.printText(isTemplate ? item.label : item.value);
					break;
				case "qrcode":
					await this.setQRCodeSize(item.qrcodeSize);
					await this.printQRCode(isTemplate ? item.label : item.value);
					await this.printTextAndFeed("", item.lines);
					break;
				case "feed":
					await this.printTextAndFeed("", item.lines);
					break;
			}
		}

	}

}
